﻿/*  
 *  IIS LogFile Analyser, Copyright (C) 2013. http://www.iis-analyser.com
 *  --
 *  This program is free software, provided "AS IS" WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
 * 
 *  File Author: Ian Quigley,    2013-02-23
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Reflection;

namespace LogParser.DTO
{
    internal class FieldAttribute : Attribute
    {
        internal string Code { get; set; }

        internal FieldAttribute(string code)
        {
            Code = code;
        }
    }

    public class LineDocument : IDocumentLine
    {
        private LineDocument()
        { }

        public DateTime Visit { get; set; }

        [Field( "c-ip" )]
        public string SourceIP { get; set; }
        [Field( "s-sitename" )]
        public string SiteName { get; set; }
        [Field( "s-computername" )]
        public string ComputerName { get; set; }
        [Field( "s-ip" )]
        public string ServerIp { get; set; }
        [Field( "s-port" )]
        public string Port { get; set; }
        [Field( "cs-method" )]
        public string Method { get; set; }
        [Field( "cs-uri-stem" )]
        public string URI { get; set; }
        [Field( "cs-uri-query" )]       
        public string Query { get; set; }
        [Field( "cs-username" )]
        public string Username { get; set; }
        [Field( "sc-status" )]
        public string Status { get; set; }
        [Field( "sc-substatus" )]
        public string SubStatus { get; set; }
        [Field( "sc-win32-status" )]
        public string Win32Status { get; set; }
        [Field( "sc-bytes" )]
        public string BytesSent { get; set; }
        [Field( "cs-bytes" )]
        public string BytesRecieved { get; set; }
        [Field( "time-taken" )]
        public string TimeTaken { get; set; }
        [Field( "cs-version" )]
        public string ProtocolVersion { get; set; }
        [Field( "cs-host" )]
        public string Host { get; set; }
        [Field( "cs(User-Agent)" )]
        public string UserAgent { get; set; }
        [Field( "cs(Cookie)" )]
        public string Cookie { get; set; }
        [Field( "cs(Referer)" )]
        public string Referer { get; set; }

        private static Dictionary<string, Tuple<Action<LineDocument, string, PropertyInfo>, PropertyInfo>> _setters;

        static LineDocument()
        {
            _setters = new Dictionary<string, Tuple<Action<LineDocument, string, PropertyInfo>, PropertyInfo>>();

            // Build field mapper setter dictionary
            var members = typeof( LineDocument ).GetProperties(BindingFlags.Instance|BindingFlags.Public);
            foreach (var member in members)
            {
                var fieldAttribute = member.GetCustomAttributes( typeof( FieldAttribute ), false ).FirstOrDefault();
                if (fieldAttribute == null)
                    continue;

                _setters.Add( ((FieldAttribute)fieldAttribute).Code, 
                    new Tuple<Action<LineDocument, string, PropertyInfo>, PropertyInfo>(
                        ( ld, str, pi ) => pi.SetValue( ld, str, null ), member ));
            }
        }

        public override string ToString()
        {
            return "Line: Visit= " + Visit + " URI= " + URI;
        }

        public static LineDocument Build( List<string> parts, DocumentHeader header)
        {
            var doc = new LineDocument();

            string date = "2001/01/01";
            string time = "00:00:00";
            bool error = false;

            for (int i = 0; i < parts.Count; i++)
            {
                if (header.Fields[i] == "date")
                {
                    date = parts[i];
                    continue;
                }

                if (header.Fields[i] == "time")
                {
                    time = parts[i];
                    continue;
                }

                if (!_setters.ContainsKey( header.Fields[i] ))
                {
                    error = true;
                    break;
                }

                var setter = _setters[header.Fields[i]];
                setter.Item1( doc, parts[i], setter.Item2 );
            }

            // Set visit date
            DateTime update;
            if (!StringParser.TryParseDateTime( date + " " + time, out update ))
                update = header.Updated;

            doc.Visit = update;

            doc.IsError = error;
            return doc;
        }

        public bool IsError { get; private set;  }

        public bool IsComment
        {
            get { return false; }
        }
    }
}
